#pragma once
#include <iostream>
#include <string>
#include <TlHelp32.h>
#include <Windows.h>

class Injector
{
public:
	HANDLE process;
	HWND hwndproc;
	DWORD_PTR clientDLL;

	DWORD_PTR GetModule(DWORD pid, const char* name)
	{
		HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
		if (snapshot == INVALID_HANDLE_VALUE)
		{
			return 0;
		}

		MODULEENTRY32W mEntry;
		mEntry.dwSize = sizeof(MODULEENTRY32W);
		do
		{
			char moduleName[MAX_PATH] = { 0 };
			WideCharToMultiByte(CP_ACP, 0, mEntry.szModule, -1, moduleName, MAX_PATH, NULL, NULL);
			if (!strcmp(moduleName, name))
			{
				CloseHandle(snapshot);
				return (DWORD_PTR)mEntry.modBaseAddr;
			}
		} while (Module32NextW(snapshot, &mEntry));
		CloseHandle(snapshot);
		return 0;
	}

	bool inject(DWORD pid, const char* dll)
	{
		if (pid == 0)
		{
			return false;
		}

		char myDLL[MAX_PATH];
		if (GetFullPathNameA(dll, MAX_PATH, myDLL, NULL) == 0)
		{
			return false;
		}

		HANDLE hProcess = OpenProcess(
			PROCESS_CREATE_THREAD |
			PROCESS_QUERY_INFORMATION |
			PROCESS_VM_READ |
			PROCESS_VM_WRITE |
			PROCESS_VM_OPERATION,
			FALSE,
			pid
		);

		if (hProcess == NULL)
		{
			return false;
		}

		LPVOID allocatedMem = VirtualAllocEx(
			hProcess,
			NULL,
			strlen(myDLL) + 1,
			MEM_RESERVE | MEM_COMMIT,
			PAGE_READWRITE
		);

		if (allocatedMem == NULL)
		{
			CloseHandle(hProcess);
			return false;
		}

		size_t bytesWritten;
		if (!WriteProcessMemory(
			hProcess,
			allocatedMem,
			myDLL,
			strlen(myDLL) + 1,
			&bytesWritten
		))
		{
			VirtualFreeEx(hProcess, allocatedMem, 0, MEM_RELEASE);
			CloseHandle(hProcess);
			return false;
		}

		LPVOID loadLibraryAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
		if (loadLibraryAddr == NULL)
		{
			VirtualFreeEx(hProcess, allocatedMem, 0, MEM_RELEASE);
			CloseHandle(hProcess);
			return false;
		}

		HANDLE hThread = CreateRemoteThread(
			hProcess,
			NULL,
			0,
			(LPTHREAD_START_ROUTINE)loadLibraryAddr,
			allocatedMem,
			0,
			NULL
		);

		if (hThread == NULL)
		{
			VirtualFreeEx(hProcess, allocatedMem, 0, MEM_RELEASE);
			CloseHandle(hProcess);
			return false;
		}

		DWORD waitResult = WaitForSingleObject(hThread, 5000);
		if (waitResult != WAIT_OBJECT_0)
		{
			VirtualFreeEx(hProcess, allocatedMem, 0, MEM_RELEASE);
			CloseHandle(hThread);
			CloseHandle(hProcess);
			return false;
		}

		CloseHandle(hThread);
		CloseHandle(hProcess);

		return true;
	}

private:
};
